package com.xmy.cultivate.service.impl;

import com.alibaba.nls.client.AccessToken;
import com.aliyun.oss.ClientException;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.xmy.cultivate.characteristic.ALiYunSmsCharacteristic;
import com.xmy.cultivate.constants.AliyunCodeConstants;
import com.xmy.cultivate.entity.Student;
import com.xmy.cultivate.entity.StudentAudio;
import com.xmy.cultivate.service.IAliyunApiService;
import com.xmy.cultivate.service.IOssUploadService;
import com.xmy.cultivate.service.IStudentAudioService;
import com.xmy.cultivate.service.IStudentService;
import com.xmy.cultivate.util.Code;
import com.xmy.cultivate.util.CommonUtil;
import com.xmy.cultivate.util.Result;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
public class AliyunApiServiceImpl implements IAliyunApiService {

    @Value("${uploadFile.location}")
    private String uploadFileLocation;//上传文件保存的本地目录，使用@Value获取全局配置文件中配置的属性值，如 E:/wmx/uploadFiles/

    @Value("${aliyun.oss.showImgEndpoint}")
    String showImgEndpoint;

    @Value("${aliyun.tts.appKey}")
    private String appkey;

    @Value("${aliyun.tts.accessKeyId}")
    private String accessKeyId;

    @Value("${aliyun.tts.accessKeySecret}")
    private String accessKeySecret;

    @Autowired
    @Lazy
    IStudentService iStudentService;

    @Autowired
    @Lazy
    IStudentAudioService iStudentAudioService;

    @Autowired
    @Lazy
    IOssUploadService iOssUploadService;

    @Resource
    ALiYunSmsCharacteristic aliyunSmsCharacteristic;

    @Autowired
    RedisTemplate<Object, Object> redisTemplate;


    @Override
    @Async("onlineAsync")
    public void studentAudioBatch() {
        QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
        studentQueryWrapper.eq("deleted", 0);
        studentQueryWrapper.and(q -> {
            q.isNull("name_audio_path").or().eq("name_audio_path", "");
        });
//        studentQueryWrapper.last("limit 1");
        List<Student> studentList = iStudentService.list(studentQueryWrapper);
        for (Student student : studentList) {
            if (student.getName() == null || student.getName().isEmpty()) {
                continue;
            }
//            System.out.println(student.getName());
            this.ttsProcessGETRequet(student);
        }
    }


    /**
     * 姓名转音频地址
     */
    @Override
    @Async("onlineAsync")
    public void ttsProcessGETRequet(Student student) {
        ttsData(student, 1);
        ttsData(student, 2);
        ttsData(student, 3);
        ttsData(student, 4);
        ttsData(student, 5);
        ttsData(student, 6);
    }

    //1姓名签到成功，2姓名签到失败，3姓名已签到
    private void ttsData(Student student, int type) {
        QueryWrapper<StudentAudio> studentAudioQueryWrapper = new QueryWrapper<>();
        studentAudioQueryWrapper.eq("student_id", student.getId());
        studentAudioQueryWrapper.eq("type", type);
        studentAudioQueryWrapper.last("LIMIT 1");
        StudentAudio audioServiceOne = iStudentAudioService.getOne(studentAudioQueryWrapper);
        if (audioServiceOne != null) {
            Student selectById = iStudentService.getById(student.getId());
            if (Objects.equals(selectById.getName(), student.getName())) {
                return;
            }
        }
        String name = "";
        if (type == 1) {
            name = "签到成功";
        } else if (type == 2) {
            name = "签到失败";
        } else if (type == 3) {
            name = "已签到";
        } else if (type == 4) {
            name = "签退成功";
        }else if (type == 5) {
            name = "交卷成功";
        } else if (type == 6) {
            name = "交卷失败";
        }
        LocalDate localDate = LocalDate.now();
        String directoryName = "name-audio-path" + "/" + localDate.getYear() + "-" + localDate.getMonthValue();//音频目录
        String uploadUrl = uploadFileLocation + "/" + directoryName;
        File file = new File(uploadUrl);
        // 若不存在则创建该文件夹
        if (!file.exists()) {
            file.mkdirs();
        }
        String date = new SimpleDateFormat("yyyyMM").format(System.currentTimeMillis());
        String format = "mp3";
        int sampleRate = 16000;
        // 采用RFC 3986规范进行urlencode编码。
        String text = student.getName() + name;
        try {
            text = URLEncoder.encode(text, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String uuid = date + System.currentTimeMillis();
        String saveName = uuid + ".mp3";
        String audioSaveFile = uploadUrl + "/" + saveName;

        /*
          设置HTTPS GET请求：
          1.使用HTTPS协议
          2.语音识别服务域名：nls-gateway-cn-shanghai.aliyuncs.com
          3.语音识别接口请求路径：/stream/v1/tts
          4.设置必须请求参数：appkey、token、text、format、sample_rate
          5.设置可选请求参数：voice、volume、speech_rate、pitch_rate
         */
        String url = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";
        url = url + "?appkey=" + appkey;
        AccessToken newAccessToken = new AccessToken(accessKeyId, accessKeySecret);
        try {
            newAccessToken.apply();
            System.out.println("Token: " + newAccessToken.getToken() + ", expire time: " + newAccessToken.getExpireTime());
            url = url + "&token=" + newAccessToken.getToken();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        url = url + "&text=" + text;
        url = url + "&format=" + format;
//        url = url + "&voice=" + voice;//要阿里云功能块设置的，不用代码传过来的
        url = url + "&sample_rate=" + sampleRate;
        // voice 发音人，可选，默认是xiaoyun。
        // url = url + "&voice=" + "xiaoyun";
        // volume 音量，范围是0~100，可选，默认50。
        // url = url + "&volume=" + String.valueOf(50);
        // speech_rate 语速，范围是-500~500，可选，默认是0。
        // url = url + "&speech_rate=" + String.valueOf(0);
        // pitch_rate 语调，范围是-500~500，可选，默认是0。
        // url = url + "&pitch_rate=" + String.valueOf(0);
//        System.out.println("URL: " + url);
        /*
          发送HTTPS GET请求，处理服务端的响应。
         */
        Request request = new Request.Builder().url(url).get().build();
        try {
//            long start = System.currentTimeMillis();
            OkHttpClient client = new OkHttpClient();
            Response response = client.newCall(request).execute();
//            System.out.println("total latency :" + (System.currentTimeMillis() - start) + " ms");
//            System.out.println(response.headers().toString());
            String contentType = response.header("Content-Type");
            if ("audio/mpeg".equals(contentType)) {
                File f = new File(audioSaveFile);
                FileOutputStream fout = new FileOutputStream(f);
                fout.write(response.body().bytes());
                fout.close();
                //上传到oss
                boolean ossUpRs = iOssUploadService.upFile(audioSaveFile, saveName, directoryName);//上传到oss
                if (ossUpRs) {
                    String ossImageUrl = showImgEndpoint + "/" + directoryName + "/" + saveName;
                    if (audioServiceOne != null) {
                        //更新音频到mysql
                        UpdateWrapper<StudentAudio> studentAudioUpdateWrapper = new UpdateWrapper<>();
                        studentAudioUpdateWrapper.eq("id", audioServiceOne.getId());
                        studentAudioUpdateWrapper.set("name_audio_path", ossImageUrl);
                        iStudentAudioService.update(null, studentAudioUpdateWrapper);
                    } else {
                        StudentAudio studentAudio = new StudentAudio();
                        studentAudio.setStudentId(student.getId());
                        studentAudio.setNameAudioPath(ossImageUrl);
                        studentAudio.setType(type);
                        iStudentAudioService.save(studentAudio);
                    }
                }
//                System.out.println("The GET request succeed!");
            } else {
                // ContentType 为 null 或者为 "application/json"
                String errorMessage = response.body().string();
                System.out.println("The GET request failed: " + errorMessage);
            }
            response.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送创建试听学员验证码
     * @param phone
     * @param adminId
     * @return
     */
    @Override
    public Result sendVerifyCode(String phone, Long adminId){

        String cacheKey = "verifyCode"+adminId+phone;
        Integer validSeconds = 300;

        Integer oftenTime = validSeconds - 60;//频繁时间，1分钟之内不能发送

        /*Long passExpire = redisTemplate.opsForValue().getOperations().getExpire(cacheKey);
        if (passExpire > oftenTime){
            //return new Result(Code.ERROR,"","请不要频繁操作，稍候再发");
        }*/
        Object code = CommonUtil.generate4DigitNumber();
        aliyunSmsCharacteristic.goToSms(accessKeyId,accessKeySecret,AliyunCodeConstants.SIGN_NAME,AliyunCodeConstants.VERIFY_TEMPLATE_CODE,phone,code.toString());
        redisTemplate.opsForValue().set(cacheKey, code,validSeconds, TimeUnit.SECONDS);
        return new Result(Code.OK,"","");
    }

    /**
     * 验证试听学员验证码
     * @param phone
     * @param verCode
     * @param adminId
     * @return
     */
    @Override
    public boolean verifyCode(String phone,String verCode,Long adminId){
        String cacheKey = "verifyCode"+adminId+phone;
        Object code = redisTemplate.opsForValue().get(cacheKey);
        if(code == null){
            return false;
        }
        if (code.toString().equals(verCode)){
            return true;
        }else {
            return false;
        }
    }


    /**
     * 发送创建试听学员验证码
     * @param phone
     * @param adminId
     * @return
     */
    @Override
    public Result sendCommonVerifyCode(String phone, Long adminId, String cachePrefix, String smsTemplateCode){

        String verifyCommonCode = "verifyCommonCode"+cachePrefix;
        String cacheKey = verifyCommonCode+adminId+phone;
        Integer validSeconds = 300;
        Integer oftenTime = validSeconds - 60;//频繁时间，1分钟之内不能发送

        Long passExpire = redisTemplate.opsForValue().getOperations().getExpire(cacheKey);
        if (passExpire > oftenTime){
            return new Result(Code.ERROR,"","请不要频繁操作，稍候再发");
        }
        Object code = CommonUtil.generate4DigitNumber();
        aliyunSmsCharacteristic.goToSms(accessKeyId,accessKeySecret,AliyunCodeConstants.SIGN_NAME,smsTemplateCode,phone,code.toString());
        redisTemplate.opsForValue().set(cacheKey, code,validSeconds, TimeUnit.SECONDS);
        return new Result(Code.OK,"","");
    }

    /**
     * 验证试听学员验证码
     * @param phone
     * @param verCode
     * @param adminId
     * @return
     */
    @Override
    public boolean verifyCommonCode(String phone, String verCode, Long adminId, String cachePrefix){
        String verifyCommonCode = "verifyCommonCode"+cachePrefix;

        String cacheKey = verifyCommonCode+adminId+phone;
        Object code = redisTemplate.opsForValue().get(cacheKey);
        if(code == null){
            return false;
        }
        if (code.toString().equals(verCode)){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 获取的安全令牌
     * @return
     */
    public String getSecurityToken(){
        // STS服务接入点，例如sts.cn-hangzhou.aliyuncs.com。您可以通过公网或者VPC接入STS服务。
        String endpoint = "sts.cn-hangzhou.aliyuncs.com";
        // 从环境变量中获取步骤1生成的RAM用户的访问密钥（AccessKey ID和AccessKey Secret）。
        String accessKeyId = System.getenv("OSS_ACCESS_KEY_ID");
        String accessKeySecret = System.getenv("OSS_ACCESS_KEY_SECRET");
        // 从环境变量中获取步骤3生成的RAM角色的RamRoleArn。
        String roleArn = System.getenv("OSS_STS_ROLE_ARN");
        // 自定义角色会话名称，用来区分不同的令牌，例如可填写为SessionTest。
        String roleSessionName = "yourRoleSessionName";
        // 临时访问凭证将获得角色拥有的所有权限。
        String policy = null;
        // 临时访问凭证的有效时间，单位为秒。最小值为900，最大值以当前角色设定的最大会话时间为准。当前角色最大会话时间取值范围为3600秒~43200秒，默认值为3600秒。
        // 在上传大文件或者其他较耗时的使用场景中，建议合理设置临时访问凭证的有效时间，确保在完成目标任务前无需反复调用STS服务以获取临时访问凭证。
        Long durationSeconds = 3600L;
        try {
            // 发起STS请求所在的地域。建议保留默认值，默认值为空字符串（""）。
            String regionId = "";
            // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
            DefaultProfile.addEndpoint(regionId, "Sts", endpoint);
            // 添加endpoint。适用于Java SDK 3.12.0以下版本。
            // DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
            // 构造default profile。
            IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
            // 构造client。
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            // 适用于Java SDK 3.12.0及以上版本。
            request.setSysMethod(MethodType.POST);
            // 适用于Java SDK 3.12.0以下版本。
            // request.setMethod(MethodType.POST);
            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
            request.setPolicy(policy);
            request.setDurationSeconds(durationSeconds);
            final AssumeRoleResponse response = client.getAcsResponse(request);
            System.out.println("Expiration: " + response.getCredentials().getExpiration());
            System.out.println("Access Key Id: " + response.getCredentials().getAccessKeyId());
            System.out.println("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
            System.out.println("Security Token: " + response.getCredentials().getSecurityToken());
            System.out.println("RequestId: " + response.getRequestId());
        } catch (ClientException | com.aliyuncs.exceptions.ClientException e) {
            System.out.println("Failed：");
            //System.out.println("Error code: " + e.getErrCode());
            //System.out.println("Error message: " + e.getErrMsg());
            //System.out.println("RequestId: " + e.getRequestId());
        }

        return null;
    }

}
